﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace CavinatorService.DAO.Base
{
    /// <summary>
    /// Classe responsável por códigos base que serão utilizados em todas as Mappers
    /// </summary>
    public abstract class BaseMapper<T> : DBAccess
    {
        #region Construtores

        /// <summary>
        /// Construtor da classe, inicializando construtor da classe DatabaseGateway
        /// </summary>        
        protected BaseMapper()
            : base()
        {

        }

        /// <summary>
        /// Construtor da classe, recebendo parâmetros de connectionString e providerName
        /// </summary>        
        protected BaseMapper(string connectionString)
            : base(connectionString)
        {
        }

        #endregion

        #region Assinaturas

        /// <summary>
        /// Nome da tabela representada pela classe
        /// </summary>
        public abstract string TableName { get; }

        /// <summary>
        /// Array contendo os campos que sao chave da tabela
        /// </summary>
        public abstract string[] Keys { get; }

        /// <summary>
        /// Atualiza um registro
        /// </summary>
        /// <param name="registro"></param>
        /// <param name="id"></param>
        public abstract void Atualizar(T registro);

        /// <summary>
        /// Insere um registro retornando o ID do registro inserido
        /// </summary>
        /// <param name="registro"></param>
        /// <returns></returns>
        public abstract void Inserir(T registro);

        /// <summary>
        /// Excluir um registro baseado no seu ID
        /// </summary>
        /// <param name="id"></param>
        public abstract void Excluir(int id);

        /// <summary>
        /// Verifica se o registro existe
        /// </summary>
        /// <returns></returns>
        //public abstract int VerificarExistencia(T registro);

        /// <summary>
        /// Listar registros
        /// </summary>
        /// <returns></returns>
        public abstract List<T> Listar();

        /// <summary>
        /// Retorna instrução SQL para inserir registro (s) na tabela <see cref="TableName"/>
        /// </summary>
        /// <param name="tabela">Tabela(s) onde será feita a inserção</param>
        /// <param name="campos">Campos do INSERT</param>
        /// <param name="valores">Valores do INSERT</param>
        /// <param name="InsertSelect">Caso seja Insert de select flag deverá vir como true</param>
        /// <returns>Instrução SQL</returns>
        public String InserirDados(string tabela, string campos, string valores, bool InsertSelect)
        {
            string tabelaConsulta = tabela ?? TableName;

            if (InsertSelect)
                return string.Format(" INSERT INTO {0} {1} {2}", tabelaConsulta, VerificaParenteses(campos), VerificaParenteses(valores));
            else
                return string.Format(" INSERT INTO {0} {1} VALUES {2}", tabelaConsulta, VerificaParenteses(campos), VerificaParenteses(valores));
        }

        /// <summary>
        /// Retorna instrução SQL para alterar registro (s) da tabela <see cref="TableName"/>
        /// </summary>
        /// <param name="tabela">Tabela(s) onde será feita a alteração</param>
        /// <param name="campoValor">Campos a serem atualizados e valores novos (Ex: IdUsuario = @IdUsuario)</param>
        /// <param name="condicao">Condicao de atualização</param>
        /// <returns>Instrução SQL</returns>
        public String AlterarDados(string tabela, string campoValor, string condicao)
        {
            string tabelaConsulta = tabela ?? TableName;

            return string.Format(" UPDATE {0} SET {1} WHERE {2}", tabelaConsulta, campoValor, condicao);
        }

        /// <summary>
        /// Retorna instrução SQL para remover registro (s) da tabela <see cref="TableName"/>
        /// </summary>
        /// <param name="tabela">Tabela(s) onde será feita a exclusão</param>
        /// <param name="condicao">Condição de exclusão</param>
        /// <returns>Instrução SQL</returns>
        public String ExcluirDados(string tabela, string condicao)
        {
            string TabelaConsulta = tabela ?? TableName;
            string CondicaoConsulta = "";

            if (!string.IsNullOrEmpty(condicao))
                CondicaoConsulta = "WHERE " + condicao;

            return string.Format(" DELETE FROM {0} {1}", TabelaConsulta, CondicaoConsulta);
        }

        /// <summary>
        /// Retorna instrução SQL para retornar os registro (s) da tabela <see cref="TableName"/>
        /// </summary>
        /// <param name="campos">Campos da consulta</param>
        /// <param name="tabela">Tabela(s) onde será feita a consulta</param>
        /// <param name="condicao">Condição de filtragem</param>
        /// <returns>Instrução SQL</returns>
        public String SelecionarDados(string campos = null, string tabela = null, string condicao = null, bool distinct = false)
        {
            campos = campos ?? "*";

            if (string.IsNullOrEmpty(tabela))
                tabela = TableName;


            string select = string.Format(" SELECT " + (distinct ? "DISTINCT" : "") + " {0} ", campos);

            if (string.IsNullOrEmpty(tabela))
                tabela = TableName;

            select += string.Format(" FROM {0} ", tabela);

            if (!string.IsNullOrEmpty(condicao))
                select += string.Format(" WHERE {0} ", condicao);

            return select;
        }

        public string ContarElementos(List<IDbDataParameter> parametros)
        {
            StringBuilder sb = new StringBuilder("SELECT COUNT(*) FROM ");
            
            sb.Append(TableName);

            sb.Append(" WHERE ");

            foreach (IDbDataParameter param in parametros)
            {
                sb.Append(param.ParameterName);
                sb.Append(" = @");
                sb.Append(param.ParameterName);
                sb.AppendLine(" AND ");
            }

            return sb.ToString(0, sb.Length - 5);

        }

        #endregion

        #region Métodos auxiliares

        /// <summary>
        /// Verifica se foi inserido "(" no início e ")" no fim da string
        /// </summary>
        /// <param name="texto">Texto a ser verificado</param>
        /// <returns>Retorna a string com os parentesses</returns>
        private string VerificaParenteses(string texto)
        {
            if (texto.Trim()[0] != '(')
                texto = "(" + texto.TrimStart();

            if (texto.Trim()[texto.Trim().Length - 1] != ')')
                texto += ")";

            return texto;
        }

        #endregion
    }
}
